﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using log4net;
using Truncation.Detector.Win32.Controls;
using Truncation.Detector.Windows;

namespace Truncation.Detector.Win32
{
    internal class Dialog : IDisposable
    {
        private static readonly ILog Log = LogManager.GetLogger("Main");
        private readonly string _name;
        private string _caption;
        private int _left;
        private int _top;
        private int _width;
        private int _height;
        private readonly CultureInfo _cultureInfo;
        private Font _font;
        private double _scale;
        private readonly List<Control> _controls = new List<Control>();
        private static readonly Regex CaptionPattern = new Regex(@"CAPTION\s+""(.*)""");
        private static readonly Regex FontPattern = new Regex(@"^FONT\s+(?<PointSize>\d+),\s*""(?<FontName>.*)""($|(,\s*(?<Weight>\d+),\s*(?<Italic>\d+),\s*(?<Charset>[0-9xX]+)$))");
        private static readonly Regex TextPattern = new Regex(@"^\s+(LTEXT)\s+""(?<Content>.*)"",\s*(.*),\s*(?<Left>\d+),\s*(?<Top>\d+),\s*(?<Width>\d+),\s*(?<Height>\d+).*$");
        private static readonly Regex ButtonPattern = new Regex(@"^\s+(DEFPUSHBUTTON|PUSHBUTTON)\s+""(?<Content>.*)"",\s*(.*),\s*(?<Left>\d+),\s*(?<Top>\d+),\s*(?<Width>\d+),\s*(?<Height>\d+).*$");
        public Dialog(string name, string frame, CultureInfo current)
        {
            _name = name;
            _cultureInfo = current;

            string[] sizes = frame.Replace(" ", String.Empty).Split(',');
            _left = Int32.Parse(sizes[0]);
            _top = Int32.Parse(sizes[1]);
            _width = Int32.Parse(sizes[2]);
            _height = Int32.Parse(sizes[3]);
        }

        public void Read(PositionReader reader)
        {
            var codePage = _cultureInfo.TextInfo.ANSICodePage;
            var encode = Encoding.GetEncoding(codePage);
            Log.InfoFormat("Measuring Dialog: {0} with the encoding: {1}", _name, encode.EncodingName);
            string line;
            while (((line = reader.ReadLine()) != null && !line.Equals("END")))
            {
                if (String.IsNullOrWhiteSpace(line))
                    continue;
                line = EncodingConvert(line, reader.CurrentEncoding, encode);
                if (_caption == null)
                {
                    Match match = CaptionPattern.Match(line);
                    if (match.Success)
                    {
                        _caption = match.Groups[1].Value;
                        continue;
                    }                    
                }
                if (_font == null)
                {
                    Match match = FontPattern.Match(line);
                    if (match.Success)
                    {
                        int weight = 0;
                        uint italic = 0;
                        uint charset = 0;
                        var fontName = match.Groups["FontName"].Value;
                        var fontSize = Int32.Parse(match.Groups["PointSize"].Value);
                        Int32.TryParse(match.Groups["Weight"].Value, out weight);
                        UInt32.TryParse(match.Groups["Italic"].Value, out italic);
                        var convertFromString = new System.ComponentModel.UInt32Converter().ConvertFromString(
                            match.Groups["Charset"].Value);
                        if (convertFromString != null)
                            charset = (uint)convertFromString;
                        _font = Font.FromHfont(CreateFont(fontName, fontSize, weight, italic, charset));
                        _scale = (fontSize*1.0)/_font.Height;
                        continue;
                    }
                }
                {
                    Match match = TextPattern.Match(line);
                    if (match.Success)
                    {
                        var control = new Label()
                        {
                            Left = Int32.Parse(match.Groups["Left"].Value),
                            Top = Int32.Parse(match.Groups["Top"].Value),
                            Width = Int32.Parse(match.Groups["Width"].Value),
                            Height = Int32.Parse(match.Groups["Height"].Value),
                            Content = match.Groups["Content"].Value,
                            LineNumber = reader.LineNumber,
                            Parent = line
                        };
                        _controls.Add(control);
                        continue;
                    }
                }
                {
                    Match match = ButtonPattern.Match(line);
                    if (match.Success)
                    {
                        var control = new Button()
                        {
                            Left = Int32.Parse(match.Groups["Left"].Value),
                            Top = Int32.Parse(match.Groups["Top"].Value),
                            Width = Int32.Parse(match.Groups["Width"].Value),
                            Height = Int32.Parse(match.Groups["Height"].Value),
                            Content = match.Groups["Content"].Value,
                            LineNumber = reader.LineNumber,
                            Parent = line
                        };
                        _controls.Add(control);
                        continue;
                    }
                }
            }
        }

        // from wrong ansi to right ansi
        private string EncodingConvert(string content, Encoding from, Encoding to)
        {
            if (!from.Equals(Encoding.Unicode) && !from.Equals(Encoding.BigEndianUnicode) &&
                !from.Equals(Encoding.UTF8) && !from.Equals(Encoding.UTF32) && !from.Equals(to))
            {
                byte[] fromBytes = from.GetBytes(content);
                return to.GetString(fromBytes);
            }
            return content;
        }

        public IEnumerable<Result> Measure()
        {
            IntPtr hWnd = IntPtr.Zero;
            IntPtr hdc = GdiWrap.GetDC(hWnd);
            IntPtr hFont = _font.ToHfont();
            IntPtr handle = GdiWrap.SelectObject(hdc, hFont);
            foreach (var control in _controls)
            {
                if (control.Measure(hdc, _scale) == false)
                {
                    var result = CreateReport(control.LineNumber, control.Parent);
                    yield return result;
                }
            }
            GdiWrap.SelectObject(hdc, handle);
            GdiWrap.ReleaseDC(hWnd, hdc);
        }

        // https://msdn.microsoft.com/en-us/library/windows/desktop/dd183499(v=vs.85).aspx
        private IntPtr CreateFont(string name, int size, int weight = 400, uint itailc = 0, uint charset = 0)
        {
            IntPtr hWnd = IntPtr.Zero;
            IntPtr hDc = GdiWrap.GetDC(hWnd);
            int lfHeight = -GdiWrap.MulDiv(size, GdiWrap.GetDeviceCaps(hDc, 90), 72);
            IntPtr hFont = GdiWrap.CreateFont(lfHeight, 0, 0, 0, weight, itailc, 0, 0, charset, 0, 0, 0, 0, name);
            GdiWrap.ReleaseDC(hWnd, hDc);
            return hFont;
        }

        private Result CreateReport(int number, string text)
        {
            Summary.Instance.Items++;
            Summary.Instance.Errors++;
            var result = new Result()
            {
                LineNumber = number,
                Content = text
            };
            return result;
        }

        public void Dispose()
        {
            if (_font != null)
                _font.Dispose();
            _font = null;
        }
    }
}
